#include "bsp_config.h"

static uint8_t pupm_state = 1;       //弹起
volatile uint32_t PumpTargetCnt = 0; //泵电机计数器目标值

/***************************这个文件主要写一些电压,温度,次数,气体浓度相关*********************************/

/**
  * @brief  根据泵电机的转动次数,来动态的改变当前计数值
  * @param  无
  * @retval 无
  */
static void IncPumpCntNum(void)
{
  static uint16_t cnt = 0;
  portMUX_TYPE pump_lock = portMUX_INITIALIZER_UNLOCKED;

  //这里还得进行中断保护******************************************

  if (ConfigParam.PumpCnt.pump_cnt_data.is_empty == NOT_EMPTY)
  { //瓶子不为空
    portENTER_CRITICAL(&pump_lock);

    if (ConfigParam.PumpCnt.pump_cnt_data.need_evacuation == NEED_EVACU)
    { //需要排气
      cnt++;
      if (cnt >= ConfigParam.FactorySetting.factory_sett_data.evacuation_times)
      { //空气排完了
        cnt = 0;
        ConfigParam.PumpCnt.pump_cnt_data.need_evacuation = NOT_NEED_EVACU;
        PumpTargetCnt -= ConfigParam.FactorySetting.factory_sett_data.evacuation_times; //排气时是通过调用SetPumpLoop函数来实现的
        PumpControl(0);                                                                 ///关泵
        PumpRunFlag = 0;                                                                //表明泵停了
        write_config_param(&ConfigParam.PumpCnt.pump_cnt_data.pumpcnt, ConfigParam.PumpCnt.pC, ConfigParam.PumpCnt.length);
      }
    }
    else
    {                                              //不需要排气
      ConfigParam.PumpCnt.pump_cnt_data.pumpcnt++; //不需要排气时这个东东才会加加
      if (ConfigParam.PumpCnt.pump_cnt_data.pumpcnt >= TIMES_OF_MEDICINE)
      {                                                        //当前药瓶已空
        ConfigParam.PumpCnt.pump_cnt_data.is_empty = IS_EMPTY; //泵了超过规定的次数,将药瓶状态设置为空
        // OpenOptionLED();   //可以开启指示灯
        PumpControl(0);  //关泵
        PumpRunFlag = 0; //表明泵停了
      }
      if (ConfigParam.PumpCnt.pump_cnt_data.pumpcnt >= PumpTargetCnt)
      {                  //泵电机,转动了指定圈数后,电机停止   PumpTargetCnt;这个数得动态的增加
        PumpControl(0);  //关泵
        PumpRunFlag = 0; //表明泵停了
      }
      write_config_param(&ConfigParam.PumpCnt.pump_cnt_data.pumpcnt, ConfigParam.PumpCnt.pC, ConfigParam.PumpCnt.length);
    }
    //这里还得进行中断保护******************************************
    portEXIT_CRITICAL(&pump_lock);
    //这里写存储函数  将一些参数存起来
  }
  if (ConfigParam.PumpCnt.pump_cnt_data.is_empty == IS_EMPTY)
  {
    PumpControl(0);  //关泵
    PumpRunFlag = 0; //表明泵停了
  }
}

/*********************************************************************************************/

/**     3.3V
 *      | 
 *      |
 *     | |
 *     | |   NTC100K		    R(表示当前NTC的电阻值)  Ro(表示常温下NTC的标称电阻值)
 *     | |
 *      |
 *      |
 *      | __________Vin            //本例接法示意图
 *      |
 *     | |
 *     | | 
 *     | |   180R				R1
 *      |
 *      |
 *     GND
 */

#define DATA3_NUM 10
/**
 *	@brief	采集DATA3_NUM次数据，剔除最大和最小，后取平均值     
 *	@param  dat：待处理的数据
 *	@retval 处理后的数据
 */
static uint16_t DataFilter3(uint16_t dat)
{
  static uint32_t dat_buff1[DATA3_NUM]; //采集用数组
  uint32_t dat_buff2[DATA3_NUM];        //运算用数组
  static int8_t i = 0;
  uint8_t j = 0;
  uint32_t sum = 0;

  ++i;
  if (i <= DATA3_NUM)
  { //对于前DATA3_NUM个数据只是取平均值
    dat_buff1[i - 1] = dat;
    for (j = 0; j < i; ++j)
    {
      sum += dat_buff1[j];
    }
    sum /= i;
  }
  else
  {
    //从DATA3_NUM个数据开始，就得去除最大值，最小值，然后取平均值
    i = DATA3_NUM + 1;
    for (j = 0; j < (DATA3_NUM - 1); ++j)
    {
      dat_buff1[j] = dat_buff1[j + 1]; //将数组的第一个元素空出来(元素依次往后挪动，最后一个被踢掉dat_buff1[0]被踢掉)
    }
    dat_buff1[j] = dat;
    for (j = 0; j < DATA3_NUM; ++j)
    {
      dat_buff2[j] = dat_buff1[j];
    }
    for (j = 0; j < DATA3_NUM; ++j)
    {
      if (dat_buff2[j] > dat_buff2[0]) //dat_buff[0]里面存max
      {
        sum = dat_buff2[0];
        dat_buff2[0] = dat_buff2[j];
        dat_buff2[j] = sum;
      }
      if (dat_buff2[j] < dat_buff2[1]) //dat_buff[1]里面存min
      {
        sum = dat_buff2[1];
        dat_buff2[1] = dat_buff2[j];
        dat_buff2[j] = sum;
      }
    }
    sum = (dat_buff2[4] + dat_buff2[5]) / 2;
  }
  return sum;
}

/**
  * @brief  获取当前NTC热敏电阻的值
  * @param  dat :原始的ADC值     这里还可以做一些文章,将精度提高
  * @retval 当前NTC电阻的值		
  */
static uint32_t get_resistence_ntc(uint32_t dat)
{
  dat = dat * 8000UL / 4096UL; //由原始的ADC值计算得出电压值   算出来的单位就是  0.1mv
  dat = DataFilter3(dat);
  //Res of NTC (Ohm) = ((VCC * R1)/Vin) - R1
  return ((33000UL * 180UL) / dat) - 180UL;
}
/**
  * @brief  由当前的NTC电阻值计算出当前的温度值
  * @param  NTC_Res : 当前的NTC电阻值
  * @retval 当前温度值
  */
static double CalculateTemperature(double NTC_Res)
{
  double temp;

  temp = NTC_Res / 100000.0;     // (R/Ro)
  temp = log(temp);              // ln(R/Ro)
  temp /= 3950.0;                // 1/B * ln(R/Ro)
  temp += 1.0 / (25.0 + 273.15); // + (1/To)
  temp = 1.0 / temp;             // Invert
  temp -= 273.15;                // convert to C

  return temp;
}

volatile float CurrentTemp = 0;

/**
  * @brief  获取当前的温度值
  * @param  无
  * @retval 当前温度值
  */
float GetTemperature(void)
{
  portMUX_TYPE GetTemp_lock = portMUX_INITIALIZER_UNLOCKED;
  uint32_t dat = NTC_Adc();

  taskENTER_CRITICAL(&GetTemp_lock);
  CurrentTemp = CalculateTemperature(get_resistence_ntc(dat));
  taskEXIT_CRITICAL(&GetTemp_lock);

  return CurrentTemp;
}

/*********************************************************************************************/

/********************************************************************************************/

/**
 * @brief 泵计数管脚初始化函数
 * @param  无
 * @retval 无
 */
static void Pump_Cnt_Init(void)
{
  gpio_config_t io_conf;

  io_conf.intr_type = GPIO_INTR_DISABLE;
  io_conf.mode = GPIO_MODE_INPUT;
  //bit mask of the pins that you want to set,e.g.GPIO18/19
  io_conf.pin_bit_mask = (1ULL << PUMP_CNT_GPIO);
  //disable pull-down mode
  io_conf.pull_down_en = 0;
  //disable pull-up mode
  io_conf.pull_up_en = 0;
  //configure GPIO with the given settings
  gpio_config(&io_conf);
}

/**
 * @brief  泵计数驱动函数
 * @param  无
 * @retval 无
 */
void Pump_Cnt_Driver(void)
{
  static uint8_t pump_state_buff = 1;

  if (pump_state_buff != pupm_state) //按键被按下或弹起(只执行变化时的值)
  {
    //备份按键的状态
    pump_state_buff = pupm_state;
    if (pupm_state == 1) //按键弹起
    {
      IncPumpCntNum(); //调用泵计数函数
    }
  }
}

/**
 * @brief  泵计数扫描函数(通过软件滤波来更新当前的按键状态  4ms调用一次)
 * @param  无
 * @retval 无
 */
void Pump_Cnt_Scan(void)
{
  static uint8_t pump_buffer = 0xFF; //软件滤波用变量

  pump_buffer = (pump_buffer << 1) | ((uint8_t)gpio_get_level(PUMP_CNT_GPIO)); //

  if ((pump_buffer & 0x03) == 0x03)
  { //按下去了    每8ms调用一次,连续2次,就是16ms
    pupm_state = 0;
  }
  else if ((pump_buffer & 0x03) == 0x00)
  { //弹起了
    pupm_state = 1;
  }
}

/**
  * @brief  传感器相关的外设初始化
  * @param  无
  * @retval 无
  */
void SensorInit(void)
{
  single_adc_config();
  Pump_Cnt_Init();
}